﻿using System;
using System.Reflection;
using JetBrains.Annotations;
using MugenMvvmToolkit.Utils;

namespace MugenMvvmToolkit.Attributes
{
    /// <summary>
    ///     Specifies the display name for a property, event, or method.
    /// </summary>
    [AttributeUsage(
        AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Event,
        AllowMultiple = false, Inherited = true)]
    public sealed class DisplayNameAttribute : Attribute
    {
        #region Constructors

        /// <summary>
        ///     Initializes a new instance of the <see cref="DisplayNameAttribute" /> class.
        /// </summary>
        public DisplayNameAttribute([NotNull]string displayName)
        {
            Should.NotBeNullOrEmpty(displayName, "displayName");
            DisplayName = displayName;
        }

        /// <summary>
        ///     Initializes a new instance of the <see cref="DisplayNameAttribute" /> class.
        /// </summary>
        public DisplayNameAttribute([NotNull]Type resourceType, [NotNull]string resourceName)
        {
            Should.NotBeNull(resourceType, "resourceType");
            Should.NotBeNullOrWhitespace(resourceName, "resourceName");
            DisplayName = (string)FindResourceAccessor(resourceName, resourceType).Invoke(null, null);
        }

        #endregion

        #region Properties

        /// <summary>
        ///     Gets or sets the display name.
        /// </summary>
        public string DisplayName { get; set; }

        #endregion

        #region Methods

        private static MethodInfo FindResourceAccessor(string resourceName, Type resourceType)
        {
#if NETFX_CORE
            PropertyInfo propertyInfo = resourceType.GetProperty(resourceName);
            if (propertyInfo == null)
                throw new InvalidOperationException(
                    string.Format("Resource with the name {0} is not found in the type of {1}.", resourceName,
                                  resourceType));
            if (propertyInfo.PropertyType != typeof(string))
                throw new InvalidOperationException(
                    string.Format("Resource with the name {0} in the type of {1}, is not string.", resourceName,
                                  resourceType));
            MethodInfo methodInfo = propertyInfo.GetMethod;
            if (methodInfo == null)
                throw new InvalidOperationException(
                    string.Format("Resource with the name {0} in the type of {1}, is not have a get method.",
                                  resourceName, resourceType));
            return methodInfo;
#else
            PropertyInfo propertyInfo = resourceType.GetProperty(resourceName,
                                                                             BindingFlags.Static | BindingFlags.Public |
                                                                             BindingFlags.NonPublic);
            if (propertyInfo == null)
                Thrower.ResourceNotFound(resourceName, resourceType);
// ReSharper disable once PossibleNullReferenceException
            if (propertyInfo.PropertyType != typeof(string))
                Thrower.ResourceIsNotString(resourceName, resourceType);

            MethodInfo methodInfo = propertyInfo.GetGetMethod(true);
            if (methodInfo == null)
                Thrower.ResourceHasNotGetter(resourceName, resourceType);
            return methodInfo;
#endif
        }

        #endregion
    }
}